MediaConvertなどを使ってHLS再生する仕組みを作ってみた
はじめに
こんにちは、中村です。
AWS Elemental MediaConvertではファイルベースで簡単に動画変換をすることができます。中でもHLSは特殊な設備が不要になっており、Webサーバーでも配信ができます。 今回は動画変換から配信までをAWS上で構築してみました。これは少しの修正とCloudformationのスタック作成作業のみで完成します。
作成してみた
コード
全てのコードはgithubに置いてあります。ローカルにPullして利用してください。 cm-nakamura-yuki/play-movie-using-aws
構成
Lambda
Lambdaを作成するにあたり、S3にソースコードをアップロードします。コード用のバケット名を決定し作成しましょう。 cfn/step1.ymlの45行目とcfn/step2.ymlの44行目にあるYOUR_CODE_BUCKET_NAMEを作成したバケット名にします。
start-movie-convert/index.tsの2行目にあるYOUR_MEDIA_CONVERT_ENDPOINTをMediaConvertのアカウントページに記載されているエンドポイントに、8行目のYOUR_AWS_ACCOUNT_IDを使用するAWSアカウントのアカウントIDに修正します。 lambda/put-dynamo/index.ts, lambda/start-movie-convert/index.tsもコンパイルし、zip化します。 zipファイル名は、cfn/step1.ymlの46行目, cfn/step2.ymlの45行目と同じにしてください。
$ zip -r NAME.zip index.ts
Cloudformationのスタックを作成
cfn/step1.yml, cfn/step2.yml, cfn/step3.ymlの順番でスタックを作成していきます。step3のYOUR_AWS_ACCOUNT_IDをスタックを作成するアカウントIDに修正してください。step3ではCloudfrontの作成も行うので時間がかかります。
HTMLのアップロード
index.htmlの14行目にあるYOUR_COGNITO_ARNにstep3で出力されているCognitoのARNに修正してください。 client配下のhtmlファイルをアップロードします。アップロード先は、dist-から始まるバケット(変換された動画が格納されるバケット)です。
MediaConvert
MediaConvertにてmp4tohlsというジョブテンプレートを作成します。S3に動画がPutされたタイミングでこのテンプレートを使ってJobを作成します。
{ "Category": "HLS", "Name": "mp4tohls", "Settings": { "OutputGroups": [ { "Name": "Apple HLS", "Outputs": [ { "ContainerSettings": { "Container": "M3U8", "M3u8Settings": { "AudioFramesPerPes": 4, "PcrControl": "PCR_EVERY_PES_PACKET", "PmtPid": 480, "PrivateMetadataPid": 503, "ProgramNumber": 1, "PatInterval": 0, "PmtInterval": 0, "Scte35Source": "NONE", "NielsenId3": "NONE", "TimedMetadata": "NONE", "VideoPid": 481, "AudioPids": [ 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492 ] } }, "VideoDescription": { "Width": 1280, "ScalingBehavior": "DEFAULT", "Height": 720, "TimecodeInsertion": "DISABLED", "AntiAlias": "ENABLED", "Sharpness": 50, "CodecSettings": { "Codec": "H_264", "H264Settings": { "InterlaceMode": "PROGRESSIVE", "NumberReferenceFrames": 3, "Syntax": "DEFAULT", "Softness": 0, "FramerateDenominator": 1, "GopClosedCadence": 1, "GopSize": 90, "Slices": 1, "GopBReference": "DISABLED", "SlowPal": "DISABLED", "SpatialAdaptiveQuantization": "ENABLED", "TemporalAdaptiveQuantization": "ENABLED", "FlickerAdaptiveQuantization": "DISABLED", "EntropyEncoding": "CABAC", "Bitrate": 5000000, "FramerateControl": "SPECIFIED", "RateControlMode": "CBR", "CodecProfile": "MAIN", "Telecine": "NONE", "FramerateNumerator": 30, "MinIInterval": 0, "AdaptiveQuantization": "HIGH", "CodecLevel": "AUTO", "FieldEncoding": "PAFF", "SceneChangeDetect": "ENABLED", "QualityTuningLevel": "SINGLE_PASS", "FramerateConversionAlgorithm": "DUPLICATE_DROP", "UnregisteredSeiTimecode": "DISABLED", "GopSizeUnits": "FRAMES", "ParControl": "INITIALIZE_FROM_SOURCE", "NumberBFramesBetweenReferenceFrames": 2, "RepeatPps": "DISABLED", "DynamicSubGop": "STATIC" } }, "AfdSignaling": "NONE", "DropFrameTimecode": "ENABLED", "RespondToAfd": "NONE", "ColorMetadata": "INSERT" }, "AudioDescriptions": [ { "AudioTypeControl": "FOLLOW_INPUT", "CodecSettings": { "Codec": "AAC", "AacSettings": { "AudioDescriptionBroadcasterMix": "NORMAL", "Bitrate": 96000, "RateControlMode": "CBR", "CodecProfile": "LC", "CodingMode": "CODING_MODE_2_0", "RawFormat": "NONE", "SampleRate": 48000, "Specification": "MPEG4" } }, "LanguageCodeControl": "FOLLOW_INPUT" } ], "OutputSettings": { "HlsSettings": { "AudioGroupId": "program_audio", "IFrameOnlyManifest": "EXCLUDE" } }, "NameModifier": "_hls" } ], "OutputGroupSettings": { "Type": "HLS_GROUP_SETTINGS", "HlsGroupSettings": { "ManifestDurationFormat": "INTEGER", "SegmentLength": 10, "TimedMetadataId3Period": 10, "CaptionLanguageSetting": "OMIT", "Destination": "s3://dist-movie-source-645332194441/", "TimedMetadataId3Frame": "PRIV", "CodecSpecification": "RFC_4281", "OutputSelection": "MANIFESTS_AND_SEGMENTS", "ProgramDateTimePeriod": 600, "MinSegmentLength": 0, "MinFinalSegmentLength": 0, "DirectoryStructure": "SINGLE_DIRECTORY", "ProgramDateTime": "EXCLUDE", "SegmentControl": "SEGMENTED_FILES", "ManifestCompression": "NONE", "ClientCache": "ENABLED", "StreamInfResolution": "INCLUDE" } } } ], "AdAvailOffset": 0, "Inputs": [ { "AudioSelectors": { "Audio Selector 1": { "Offset": 0, "DefaultSelection": "DEFAULT", "ProgramSelection": 1 } }, "VideoSelector": { "ColorSpace": "FOLLOW" }, "FilterEnable": "AUTO", "PsiControl": "USE_PSI", "FilterStrength": 0, "DeblockFilter": "DISABLED", "DenoiseFilter": "DISABLED", "TimecodeSource": "EMBEDDED" } ] } }
テスト
ここまで完成したらsrc-から始まるS3バケットに動画をアップロードしてみます。S3のPutを検知しLambdaがジョブを作成します。
ジョブが完了すると、dist-から始まるバケットへ変換された動画が作成されます。またdist-から始まるバケットに_hls.m3u8のファイルがPutされたのをトリガーにDynamoDBに書き込みます。
ブラウザから確認してみる
Cloudfrontのドメインにアクセスします。index.htmlはDynamoDBに格納されているデータを取得しリスト表示にします。
リンクをクリックするとview.htmlに遷移します。hls再生のためにhls.jsを利用しています。hls.jsは簡単に実装できサポート領域も広いです。(と思っています。)
hls.jsのサポート範囲
- Chrome for Android 34+
- Chrome for Desktop 34+
- Firefox for Android 41+
- Firefox for Desktop 42+
- IE11+ for Windows 8.1+
- Edge for Windows 10+
- Opera for Desktop
- Vivaldi for Desktop
- Safari for Mac 8+ (beta)
iOSのSafariは、残念ながら対象外(hls.jsを使わずにHLSが見れる)のため別の実装方法をしています。詳しくはclient/view.htmlを参照ください。
Playerが表示され動画もロードされているのを確認できました。
まとめ
初めてMediaConvertを使ってみました。 Elastic Transcoderもありますが、標準的な変換はこちらで十分かなと思います。
弊社では、「Amazon Connect」のキャンペーンを開催しています。
また音声を中心とした各種ソリューションの開発支援も行なっております。